﻿using System;
using ContactCenter.Core.DomainModel;
using NHibernate.Event;
using NHibernate.Persister.Entity;
using NHibernate.Engine;

namespace ContactCenter.EncryptionEventListener
{
	/// <summary>
	/// The following class leverage an example of modifying the Entity inside Event Listeners.
	/// </summary>
	/// <see cref="http://ayende.com/Blog/archive/2009/04/29/nhibernate-ipreupdateeventlistener-amp-ipreinserteventlistener.aspx"/>
	public class EncryptionListener : IPreInsertEventListener, IPostLoadEventListener, IPreUpdateEventListener
	{
		// DO NOT DO THIS! DO NOT store the key inside the code. This is just an example!
		private const string Password = "DotDotNet";

		private static void EncryptCreditCard(Customer customer)
		{
			CreditCard cc = customer.CreditCard;

			string encryptedPrivatePart = EncryptToBase64(cc.PrivatePart);
			CreditCard encryptedCreditCard = new CreditCard(customer.CreditCard.PublicPart, encryptedPrivatePart, cc.ExpirationDate);

			customer.ChangeCreditCard(encryptedCreditCard);
		}

		private static void SetEntityState(IEntityPersister persister, object[] state, string propertyName, object value)
		{
			var index = Array.IndexOf(persister.PropertyNames, propertyName);
			if (index == -1)
				return;
			state[index] = value;
		}

		private static string EncryptToBase64(string clearText)
		{
			string encryptedText = EncryptionHelper.Encrypt(clearText, Password);

			return encryptedText;
		}

		private static string DecryptFromBase64(string encryptedText)
		{
			string clearText = EncryptionHelper.Decrypt(encryptedText, Password);

			return clearText;
		}

		#region IPreInsertEventListener Members

		public bool OnPreInsert(PreInsertEvent evt)
		{
			if (evt.Entity is Customer)
			{
				Customer cust = (evt.Entity as Customer);

				EncryptCreditCard(cust);

				SetEntityState(evt.Persister, evt.State, "CreditCard", cust.CreditCard);
			}

			return false;
		}

		#endregion

		#region IPostLoadEventListener Members

		public void OnPostLoad(PostLoadEvent evt)
		{
			if (evt.Entity.GetType() == typeof(Customer))
			{
				Customer cust = (evt.Entity as Customer);
				if (cust != null)
				{
					CreditCard cc = cust.CreditCard;

				    string decryptedPrivatePart = string.Empty;

                    try
                    {
                        decryptedPrivatePart = DecryptFromBase64(cc.PrivatePart);
                    }
                    catch (Exception)
                    {
                        decryptedPrivatePart = cc.PrivatePart;
                    }

					CreditCard decryptedCreditCard = new CreditCard(cust.CreditCard.PublicPart, decryptedPrivatePart, cc.ExpirationDate);

					cust.ChangeCreditCard(decryptedCreditCard);

					EntityEntry entry = evt.Session.PersistenceContext.GetEntry(evt.Entity);
					SetEntityState(evt.Persister, entry.LoadedState, "CreditCard", cust.CreditCard);
				}
			}        
		}

		#endregion

		#region IPreUpdateEventListener Members

		public bool OnPreUpdate(PreUpdateEvent evt)
		{
			if (evt.Entity is Customer)
			{
				int[] dirtyProperties = evt.Persister.FindDirty(evt.State, evt.OldState, evt.Entity, evt.Session);

				foreach (int idx in dirtyProperties)
				{
					if (evt.Persister.PropertyNames[idx].ToUpper() == "CREDITCARD")
					{
						EncryptCreditCard(evt.Entity as Customer);
						SetEntityState(evt.Persister, evt.State, "CreditCard", (evt.Entity as Customer).CreditCard);
					}                
				}
										 
			}

			return false;
		}

		#endregion
	}
}
